package com.gdones.gpt.bus.gptapi.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.gdones.common.core.dto.MyResult;
import com.gdones.gpt.bus.gptapi.dto.GptChatDto;
import com.gdones.gpt.bus.gptapi.dto.GptVideoTraclateDto;
import com.gdones.gpt.bus.gptstaff.dto.StaffModeGrant;
import com.gdones.gpt.bus.gptstaff.mapper.StaffModeGrantMapper;
import com.gdones.gpt.bus.gptstaff.service.IStaffService;
import com.gdones.gpt.bus.webAccount.mapper.GptWebaccountInfoMapper;
import com.gdones.gpt.bus.gptapi.service.GptApiService;
import com.gdones.gpt.core.util.MyRedisUtil;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 高
 * 2023/5/30
 */
@Service
@RefreshScope
@Transactional
public class GptServiceImpl implements GptApiService {

    @Autowired
    private GptWebaccountInfoMapper infoMapper;

    @Value("${mygpt.gptpath}")
    private String gptpath;

    @Value("${mygpt.gptimg}")
    private String gptimg;

    @Value("${mygpt.stability}")
    private String stability;

    @Value("${mygpt.stabilityapikey}")
    private String stabilityapikey;

    @Value("${mygpt.pollinationspath}")
    private String pollinationspath;

    private final OkHttpClient client;

    @Autowired
    private MyRedisUtil redisUtil;

    @Resource(name = "staffServiceImpl")
    private IStaffService staffService;

    @Autowired
    private StaffModeGrantMapper staffModeGrantMapper;

    public GptServiceImpl() {
        TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }};
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
        } catch (NoSuchAlgorithmException e) {
            System.err.println("--------------------------HTTPS--证书出错！");
            e.printStackTrace();
        }
        try {
            sslContext.init(null, trustManagers, new SecureRandom());
        } catch (KeyManagementException e) {
            System.err.println("--------------------------HTTPS--证书初始化出错！");
            e.printStackTrace();
        }

        this.client = new OkHttpClient.Builder()
                .connectTimeout(0, TimeUnit.SECONDS)
                .readTimeout(0, TimeUnit.SECONDS)
                .writeTimeout(0, TimeUnit.SECONDS)
                .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
                .build();
    }



    @Override
    public MyResult getGptModels() {

        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        System.err.println("------------------PATH:"+gptpath);
        Request request = new Request.Builder()
                .url(gptpath+"v1/models")
                .addHeader("Authorization","Bearer "+redisUtil.get("nowApiKey"))
                .addHeader("content-type","application/json")
                .build();


        try {
            Response response = client.newCall(request).execute();

            resMap = (Map) JSON.parse(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("GPT-获取模型列表-异常！");
            result.setMsg("GPT-获取模型列表-异常！");
            result.setCode("9999");
            return result;
        }

        result.setMsg("模型查询成功！");
        result.setData(resMap.get("data"));

        return result;
    }

    @Override
    public MyResult chat(String staffModesID,GptChatDto dto) {

        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        // 先核验一下是否能继续调用
        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        if(grant.getUseLess() == 0){
            result.setCode("9999");
            result.setMsg("今日调用次数已用完，请明日再来或升级会员哟~");
            return result;
        }

        System.err.println("------------------PATH（POST）chat聊天:"+gptpath);

        Gson gson = new Gson();
        String json = gson.toJson(dto);

// 创建请求体
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);

        Request request = new Request.Builder()
                .url(gptpath+"v1/chat/completions")
                .post(requestBody)
                .addHeader("Authorization","Bearer "+redisUtil.get("nowApiKey"))
                .build();


        try {
            Response response = client.newCall(request).execute();

            resMap = (Map) JSON.parse(response.body().string());

            if(resMap.get("error")!=null){
                Map error = (Map) resMap.get("error");
                if(error.get("type").equals("insufficient_quota")){
                    redisUtil.set("gptapis_index",(Integer)redisUtil.get("gptapis_index")+1);
                    redisUtil.set("nowApiKey",((List)redisUtil.get("gptapis")).get((Integer) redisUtil.get("gptapis_index")));
                    chat(staffModesID,dto);
                }else{
                    result.setMsg(error.get("message")+"！");
                    result.setCode("9999");
                    return result;
                }
            }


        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("GPT-ai聊天接口-异常！");
            result.setMsg("GPT-ai聊天接口-异常！");
            result.setCode("9999");
            return result;
        }

        // 调用完成更新次数
        MyResult useLessRes = staffService.updateStaffUseLess(staffModesID);
        Map<String,Object> webResMap = new HashMap<>();
        webResMap.put("webres",resMap);
        webResMap.put("useLess",useLessRes.getData());
        result.setCode(useLessRes.getCode());
        result.setMsg(useLessRes.getMsg());
        result.setData(webResMap);

        return result;
    }

    @Override
    public MyResult chatImg(String prompt,String staffModesID) {

        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        // 先核验一下是否能继续调用
        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        if(grant.getUseLess() == 0){
            result.setCode("9999");
            result.setMsg("今日调用次数已用完，请明日再来或升级会员哟~");
            return result;
        }

        System.err.println("------------------PATH（POST）gpt文生图:"+gptpath);

        Map<String,Object> paramMap = new HashMap<>();

        paramMap.put("prompt",prompt);
        paramMap.put("n",1);
        paramMap.put("size","1024x1024");

        Gson gson = new Gson();
        String json = gson.toJson(paramMap);

// 创建请求体
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);

        Request request = new Request.Builder()
                .url(gptpath+"v1/images/generations")
                .post(requestBody)
                .addHeader("Authorization","Bearer "+redisUtil.get("nowApiKey"))
                .build();


        try {
            Response response = client.newCall(request).execute();
            resMap = (Map) JSON.parse(response.body().string());
            if(resMap.get("error")!=null){
                Map error = (Map) resMap.get("error");
                if(error.get("type").equals("insufficient_quota")){
                    redisUtil.set("gptapis_index",(Integer)redisUtil.get("gptapis_index")+1);
                    redisUtil.set("nowApiKey",((List)redisUtil.get("gptapis")).get((Integer) redisUtil.get("gptapis_index")));
                    chatImg(prompt,staffModesID);
                }else{
                    result.setMsg(error.get("message")+"！");
                    result.setCode("9999");
                    return result;
                }
            }else{
                List<Map> data = (List<Map>) resMap.get("data");
                String url = (String) data.get(0).get("url");
                url = url.replaceAll("https://oaidalleapiprodscus.blob.core.windows.net",gptimg);

                try{
                    Request imgUtl = new Request.Builder()
                            .url(url).build();
                    Response imgResponse = client.newCall(imgUtl).execute();
                    ResponseBody imgbody = imgResponse.body();
                    byte[] imgbytes = imgbody.bytes();
                    System.err.println("-----DALL.E图片读取："+imgbytes.length);
                    String s = Base64.getEncoder().encodeToString(imgbytes);

                    // 调用完成更新次数
                    MyResult useLessRes = staffService.updateStaffUseLess(staffModesID);
                    Map<String,Object> webResMap = new HashMap<>();
                    webResMap.put("webres",s);
                    webResMap.put("useLess",useLessRes.getData());
                    result.setMsg(null);
                    result.setData(webResMap);
                }catch (Exception e){
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    e.printStackTrace();
                    System.err.println("GPT-gpt文生图-异常！");
                    result.setMsg("GPT-gpt文生图-异常！");
                    result.setCode("9999");
                    return result;
                }

            }

        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("GPT-gpt文生图-异常！");
            result.setMsg("GPT-gpt文生图-异常！");
            result.setCode("9999");
            return result;
        }

        return result;
    }

    @Override
    public MyResult stabilityImg(String prompt,String staffModesID) {
        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        System.err.println("------------------PATH（POST）stability文生图:"+stability);

        Map<String,Object> paramMap = new HashMap<>();

        List<Map> sonList = new ArrayList<>();
        Map<String,Object> sonMap = new HashMap<>();
        sonMap.put("text",cnToEnByGPT(prompt));
        sonList.add(sonMap);
        paramMap.put("text_prompts",sonList);
        paramMap.put("cfg_scale",7);
        paramMap.put("clip_guidance_preset","FAST_BLUE");
        paramMap.put("height","512");
        paramMap.put("width","512");
        paramMap.put("samples",1);
        paramMap.put("steps",30);

        Gson gson = new Gson();
        String json = gson.toJson(paramMap);

// 创建请求体
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);

        Request request = new Request.Builder()
                .url(stability+"v1/generation/stable-diffusion-v1-5/text-to-image")
                .post(requestBody)
                .addHeader("Accept","application/json")
                .addHeader("Content-Type","application/json")
                .addHeader("Authorization","Bearer "+stabilityapikey)
                .build();


        try {
            Response response = client.newCall(request).execute();



            if(resMap.get("error")!=null){
                Map error = (Map) resMap.get("error");
                result.setMsg("请求内容非法,请按照平台规则调用！");
                result.setCode("9999");
                return result;
            }else{
                resMap = (Map) JSON.parse(response.body().string());
                List<Map> data = (List<Map>) resMap.get("artifacts");

                // 调用完成更新次数
                MyResult useLessRes = staffService.updateStaffUseLess(staffModesID);
                Map<String,Object> webResMap = new HashMap<>();
                webResMap.put("webres",data);
                webResMap.put("useLess",useLessRes.getData());
                result.setMsg(null);
                result.setData(webResMap);
            }

        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("stabilit接口-文生图-异常！");
            result.setMsg("stabilit接口-文生图-异常！");
            result.setCode("9999");
            return result;
        }

        return result;
    }

    @Override
    public MyResult pollinationsImg(String prompt,String staffModesID) {
        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        // 先核验一下是否能继续调用
        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        if(grant.getUseLess() == 0){
            result.setCode("9999");
            result.setMsg("今日调用次数已用完，请明日再来或升级会员哟~");
            return result;
        }

        String param = cnToEnByGPT(prompt);

        System.err.println("------------------PATH:"+pollinationspath);
        Request request = new Request.Builder()
                .url(pollinationspath+"prompt/"+param)
                .build();

        try {
            Response response = client.newCall(request).execute();
            ResponseBody body = response.body();
            byte[] bytes = body.bytes();
            System.err.println("-----pollinationspath图片读取："+bytes.length);
            String s = Base64.getEncoder().encodeToString(bytes);

            // 调用完成更新次数
            MyResult useLessRes = staffService.updateStaffUseLess(staffModesID);
            Map<String,Object> webResMap = new HashMap<>();
            webResMap.put("webres",s);
            webResMap.put("useLess",useLessRes.getData());
            result.setMsg(null);
            result.setData(webResMap);

        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("pollinations-文生图-异常！");
            result.setMsg("pollinations-文生图-异常！");
            result.setCode("9999");
            return result;
        }



        return result;
    }

    @Override
    public MyResult gptVidoTraceLate(MultipartFile file, Boolean isEnglish , String staffModesID) {

        MyResult result = new MyResult();

        Map<String,Object> resMap = new HashMap<>();

        // 先核验一下是否能继续调用
        StaffModeGrant grant = staffModeGrantMapper.selectById(staffModesID);
        if(grant.getUseLess() == 0){
            result.setCode("9999");
            result.setMsg("今日调用次数已用完，请明日再来或升级会员哟~");
            return result;
        }

        System.err.println("------------------PATH（POST）GPT视频翻译:"+gptpath);
        File tempFile = null;
        try {
            tempFile = convertMultipartFileToFile(file);
        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("GPT-视频翻译接口-异常-上传文件异常！");
            result.setMsg("GPT-视频翻译接口-上传文件异常！");
            result.setCode("9999");
            return result;
        }
        System.out.println("-------------"+tempFile.getName());
        MediaType mediaType = MediaType.parse("application/octet-stream");
        RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
                .addFormDataPart("file",tempFile.getName(),RequestBody.create(tempFile, mediaType)) //RequestBody.create(tempFile, mediaType)
                .addFormDataPart("model","whisper-1")
                .build();

// 创建请求体
        Request request = new Request.Builder()
                .url(gptpath+"v1/audio/transcriptions")
                .post(body)
                .addHeader("Authorization","Bearer "+redisUtil.get("nowApiKey"))
                .build();

        try {
            Response response = client.newCall(request).execute();

            resMap = (Map) JSON.parse(response.body().string());

            if(resMap.get("error")!=null){
                Map error = (Map) resMap.get("error");
                if(error.get("type").equals("insufficient_quota")){
                    redisUtil.set("gptapis_index",(Integer)redisUtil.get("gptapis_index")+1);
                    redisUtil.set("nowApiKey",((List)redisUtil.get("gptapis")).get((Integer) redisUtil.get("gptapis_index")));
                    gptVidoTraceLate(file,isEnglish,staffModesID);
                }else{
                    result.setMsg(error.get("message")+"！");
                    result.setCode("9999");
                    return result;
                }
            }


        } catch (IOException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
            System.err.println("GPT-视频翻译接口-异常！");
            result.setMsg("GPT-视频翻译接口-异常！");
            result.setCode("9999");
            return result;
        }

        // 调用完成更新次数
        MyResult useLessRes = staffService.updateStaffUseLess(staffModesID);
        Map<String,Object> webResMap = new HashMap<>();
        webResMap.put("webres",resMap);
        webResMap.put("useLess",useLessRes.getData());
        result.setCode(useLessRes.getCode());
        result.setMsg(useLessRes.getMsg());
        result.setData(webResMap);

        return result;
    }

    /* ------------------工具--------------------------  */
    private String cnToEnByGPT(String enStr){

        Map<String,Object> resMap = new HashMap<>();

        System.err.println("------------------PATH（POST）GPT翻译:"+gptpath);
        Map<String,Object> paramMap = new HashMap<>();
        List<Map<String,Object>> sonList = new ArrayList<>();
        Map<String,Object> sonMap = new HashMap<>();
        sonMap.put("role","user");
        sonMap.put("content","现在你是一个中文翻译英语的翻译官，我给你一个中文句子你给我翻译为英文，不要出现别的话语，中文句子是："+enStr);
        sonList.add(sonMap);

        paramMap.put("model","gpt-3.5-turbo");
        paramMap.put("messages",sonList);

        Gson gson = new Gson();
        String json = gson.toJson(paramMap);

// 创建请求体
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json);

        Request request = new Request.Builder()
                .url(gptpath+"v1/chat/completions")
                .post(requestBody)
                .addHeader("Authorization","Bearer "+redisUtil.get("nowApiKey"))
                .build();

        try {
            Response response = client.newCall(request).execute();

            resMap = (Map) JSON.parse(response.body().string());

            if(resMap.get("error")!=null){
                return enStr;
            }else{
                List choices = (List) resMap.get("choices");
                Map map = (Map) choices.get(0);
                Map map2 = (Map) map.get("message");
                String resStr = (String) map2.get("content");
                return resStr;
            }


        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("GPT-翻译接口-异常！");
        }

        return enStr;

    }

    private File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException {
        Path tempDir = Files.createTempDirectory("");
        File tempFile = tempDir.resolve(multipartFile.getOriginalFilename()).toFile();
        try (InputStream is = multipartFile.getInputStream()) {
            Files.copy(is, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        }
        return tempFile;
    }

}
